package ru.compscicenter.databases.zaikina.httpserver;

import org.json.simple.JSONArray;
import ru.compscicenter.databases.zaikina.common.commands.*;
import ru.compscicenter.databases.zaikina.common.model.Node;
import ru.compscicenter.databases.zaikina.common.model.NodeConfig;
import ru.compscicenter.databases.zaikina.nodes.NodeMain;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedList;

public class HttpCommandProcessor {
    private String response;
    private final NodeConfig config;

    public HttpCommandProcessor(Command command, NodeConfig config) {
        this.config = config;
        this.response = processCommand(command);
        //this.config = config;
    }

    public String getResponse() {
        return response;
    }

    private String processCommand(Command command) {
        if (command.getCommandType() == CommandTypes.SEND_PAGE) {
            return sendPage(command.getInput());
        } else if (command.getCommandType() == CommandTypes.FIND_IN_BASE) {
            return findInDatabase(command.getInput());
        } else if (command.getCommandType() == CommandTypes.INSERT_TO_BASE) {
            return insertToDatabase(command.getInput());
        } else if (command.getCommandType() == CommandTypes.GET_DOCUMENTS) {
            return getDocuments(command.getInput());
        }
        return doNothing();
    }

    private String doNothing() {
        return "Unknown request";
    }

    private String sendPage(String page) {
         return getHtmlPage(page);
    }

    private String makeRequest(String address, Integer port, String request) {
        try {
            Socket socket = new Socket(address, port);
            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
            ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
            out.writeObject(request);
            out.flush();
            String answer = readAnswer(in);
            in.close();
            out.close();
            socket.close();
            return answer;
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private String getDocuments(String str) {
        Node node = config.getVocabularyNode(str);
        if (node == null) {
            System.out.println("Incorrect symbol in the request");
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }

        String answer = makeRequest(node.getAddress(), node.getPort(), formStringListCommand(str, SysCommandType.GetWordsWithReferences).toJSONString());
        if (answer == null || answer.equals("")) {
            System.out.println("Empty answer from vocabulary node");
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }

        FullWordsCommand command = FullWordsCommand.parse(answer);
        if (command == null) {
            System.out.println("Unexpected answer from vocabulary node" + answer);
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }

        node = config.getFileStorageNode();
        if (node == null) {
            System.out.println("Can not find file storage node");
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }
        String request = new FullWordsCommand(SysCommandType.GetFilesByWords, command.getBody(), -1).toJSONString();
        answer = makeRequest(node.getAddress(), node.getPort(), request);

        if (answer == null || answer.equals("")) {
            System.out.println("Empty answer from file storage node");
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }

        return answer;
    }

    private String findInDatabase(String str) {
        Node node = config.getVocabularyNode(str);
        if (node == null) {
            System.out.println("Incorrect symbol in the request");
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }

        Socket socket;
        try {
            socket = new Socket(node.getAddress(), node.getPort());
            //socket = new Socket("127.0.0.1", 10003);
            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
            ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
            out.writeObject(formStringListCommand(str, SysCommandType.GetSameWords).toJSONString());
            out.flush();
            String answer = readAnswer(in);
            in.close();
            out.close();
            socket.close();
            return answer;
        } catch (IOException ex) {
            ex.printStackTrace();
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }
    }

    private String readAnswer(ObjectInputStream in) throws IOException {
        String text;
        try {
            text = (String) in.readObject();
        } catch (ClassNotFoundException ex) {
            return ex.getMessage();
        }
        return text;
    }

    private StringListCommand formStringListCommand(String str, Integer commandType) {
        ArrayList<String> arr = new ArrayList<String>();
        arr.add(str);
        return new StringListCommand(commandType, arr, -1);
    }

    private String insertToDatabase(String str) {

        Node node = config.getFileStorageNode();
        Socket socket;
        try {
            socket = new Socket(node.getAddress(), node.getPort());
            //socket = new Socket("127.0.0.1", 10003);
            ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
            ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
            String request = formStringListCommand(str, SysCommandType.AddFiles).toJSONString();
            out.writeObject(request);
            out.flush();
            String answer = readAnswer(in);
            in.close();
            out.close();
            socket.close();
            return answer;
        } catch (IOException ex) {
            ex.printStackTrace();
            return new EmptyCommand(SysCommandType.Error).toJSONString();
        }
    }

    private String getHtmlPage(String file) {
        String result = "";
        BufferedReader reader = null;
        int i = 0;
        while (true) {
            try {
                reader = new BufferedReader(new FileReader(file));
                break;
            } catch (FileNotFoundException ex) {
                if (i == 0) {
                    file = "src/" + file;
                    ++i;
                    continue;
                } else {
                    break;
                }
            }
        }

        if (reader == null) {
            return "Server error. Cannot load file: " + file;
        }

        String line;
        //String result = "";
        do {
            try {
                line = reader.readLine();
                if (line != null) {
                    result += line + "\r\n";
                }
            } catch (IOException ex) {
                break;
            }
        } while (line != null);

        return result;
    }

    private String jsonArrToString(JSONArray arr) {
        StringWriter out = new StringWriter();
        try {
            arr.writeJSONString(out);
        } catch (IOException ex) {
            return "";
        }
        return out.toString();
    }
}
